package weka.roleminers;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;

import weka.core.Attribute;
import weka.core.Capabilities;
import weka.core.Instances;
import weka.core.OptionHandler;

public class CompleteRoleMiner extends AbstractRoleMiner implements
		OptionHandler {

	private StringBuilder output;

	/**
	 * 
	 */
	private static final long serialVersionUID = 9163807126248397079L;

	/**
	 * The instances (transactions) to be used for generating the association
	 * rules.
	 */
	private Instances instances;

	/**
	 * The instances (transactions) to be used for generating the association
	 * rules.
	 */
	protected List<User> users;
	protected List<Role> initial_roles;
	protected List<Role> generated_roles;

	public CompleteRoleMiner() {
		this.users = new ArrayList<User>();
		this.initial_roles = new ArrayList<Role>();
		this.generated_roles = new ArrayList<Role>();
		this.output = new StringBuilder();
	}

	@Override
	public Enumeration listOptions() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setOptions(String[] options) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	public String[] getOptions() {
		return new String[0];
	}

	@Override
	public void mineRoles(Instances data) throws Exception {
		this.instances = data;
		this.loadUsers();
		this.setInitRoles();
		this.setGenRoles();
		this.countUsersWithRole();
		this.showGenRoles();
	}

	private void countUsersWithRole() {
		for (Role iRole : generated_roles) {
			iRole.resetUsers();
			for (Role jRole : initial_roles) {
				if (jRole.contains(iRole)) {
					iRole.incrementUsers(jRole.users());
				}
			}
		}
	}

	@Override
	public Capabilities getCapabilities() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String toString() {
		return output.toString();
	}

	/**
	 * Removes columns that are all missing from the data
	 * 
	 * @param instances
	 *            the instances
	 * @return a new set of instances with all missing columns removed
	 * @throws Exception
	 *             if something goes wrong
	 */
	protected Instances removeMissingColumns(Instances instances)
			throws Exception {
		return instances;
	}

	/**
	 * Gets a user from or creates one
	 * 
	 * @param name
	 * @return
	 */
	public User getUser(String name) {
		User user;
		for (User uaux : users) {
			if (uaux.getName().equals(name))
				return uaux;
		}
		user = new User(name);
		users.add(user);
		return user;
	}

	/**
	 * Loads all permitions and users.
	 */
	public void loadUsers() {
		Attribute att;
		String name;
		User user;
		for (int i = 0; i < this.instances.numAttributes(); i++) {
			// Aca obtengo uno de los estados (actividad) para ver que usuarios
			// la realizaron
			att = instances.attribute(i);
			name = att.name();// nombre del permiso a agregar.
			for (int j = 0; j < att.numValues(); j++) {
				user = getUser(att.value(j));
				user.addPermition(name);

			}
		}
		showUsers();
	}

	private void showUsers() {
		for (User user : users) {
			output.append("User:");
			output.append(user.getName());
			output.append("        Permitions:");
			for (String permition : user.getPermitions()) {
				output.append(permition);
				output.append(",");
			}
			output.append(".");
			output.append("\n");
		}
	}

	private void showGenRoles() {
		int i = 1;
		for (Role role : generated_roles) {
			output.append("Role:");
			output.append(i);
			output.append("        Permitions:");
			for (String permition : role.getPermitions()) {
				output.append(permition);
				output.append(",");
			}
			output.append("  Users with this role:");
			output.append(role.users());
			output.append(".");
			output.append("\n");
			i++;
		}
	}

	/**
	 * Identify roles from users. Cluster users into initial roles based on
	 * exact match of the set of permissions.
	 */
	public void setInitRoles() {
		for (User user : users) {
			createRole(user.getPermitions(), initial_roles);
		}
	}

	/**
	 * Creates one role and adds it to the role list. If role already exists, it
	 * increments the users that have this role
	 * 
	 * @param permitions
	 * @param roles
	 */
	public void createRole(List<String> permitions, List<Role> roles) {
		if (!permitions.isEmpty()) {
			boolean found = false;
			Iterator<Role> rolesIterator = roles.iterator();
			Role role = null;
			while (rolesIterator.hasNext() && !found) {
				role = rolesIterator.next();
				if (role.getPermitions().equals(permitions)) {
					role.incrementUsers();
					found = true;
				}
			}
			if (!found) {
				roles.add(new Role(permitions));
			}
		}
	}

	/**
	 * Calculates interesting roles.
	 */
	public void setGenRoles() {
		Role iRol;
		Role jRol;
		for (int i = 0; i < initial_roles.size() - 1; i++) {
			iRol = initial_roles.get(i);
			for (int j = 0; j < initial_roles.size(); j++) {
				jRol = initial_roles.get(j);
				if (j != i) {
					createRole(getIntersectionRol(iRol, jRol), generated_roles);
				}
			}
			for (int k = 0; k < generated_roles.size(); k++) {
				jRol = generated_roles.get(k);
				createRole(getIntersectionRol(iRol, jRol), generated_roles);
			}
		}

	}

	/**
	 * Calculates the intersection of two roles.
	 * 
	 * @param iRol
	 * @param jRol
	 * @return permitions in common
	 */
	public List<String> getIntersectionRol(Role iRol, Role jRol) {
		List<String> solution = new ArrayList<String>();
		copy(solution, iRol.getPermitions());
		solution.retainAll(jRol.getPermitions());
		return solution;
	}

	/**
	 * Copies one list to the other.
	 * 
	 * @param destination
	 * @param source
	 */
	public void copy(List<String> destination, List<String> source) {
		for (String value : source) {
			destination.add(value);
		}
	}

}
